#include "array.h"

#define METATYPE_ARRAY "LuaBook.array"
typedef struct NumArray
{
	int size;
	double values[0];
}NumArray;

static int newarray(lua_State *L)
{
	int n = luaL_checkint(L, 1);
	size_t nbytes = sizeof(NumArray) + n * sizeof(double);
	NumArray* a = (NumArray*)lua_newuserdata(L, nbytes);

	luaL_getmetatable(L, METATYPE_ARRAY);
	lua_setmetatable(L, -2);
	a->size = n;
	stackDump(L);
	return 1;
}

static NumArray* checkarray(lua_State* L)
{
	void*  udata = luaL_checkudata(L, 1, METATYPE_ARRAY);
	luaL_argcheck(L, udata != NULL, 1, "`array' expected");

	return (NumArray*)udata;
}

static double* getelem(lua_State* L)
{
	NumArray* a =checkarray(L);
	int index = luaL_checkint(L, 2);
	
	luaL_argcheck(L, index >=1 && index <= a->size, 2, "index out of range");

	return &(a->values[index-1]);
}

static int setarray(lua_State *L)
{
	double value = luaL_checknumber(L, 3);
	*getelem(L) = value;
	//stackDump(L);
	return 0;
}

static int getarray(lua_State* L)
{
	lua_pushnumber(L, *getelem(L));
	stackDump(L);
	return 1;
}

static int getsize(lua_State* L)
{
	NumArray* a = checkarray(L);

	lua_pushnumber(L, a->size);
	stackDump(L);
	return 1;
}

static const struct luaL_reg arraylib_method[] = 
{
	{"set", setarray},
	{"get", getarray},
	{"size", getsize},
	{NULL, NULL}
};

static const struct luaL_reg arraylib_fun[] = 
{
	{"new", newarray},
	{NULL, NULL}
};

int luaopen_array(lua_State *L)
{
	luaL_newmetatable(L, METATYPE_ARRAY);
	lua_pushstring(L, "__index");

    lua_pushvalue(L, -2);    /* pushes the metatable */
    lua_settable(L, -3); /* metatable.__index = metatable */

    luaL_openlib(L, NULL, arraylib_method, 0);

	luaL_openlib(L, "array", arraylib_fun, 0);
	return 1;
}

void stackDump(lua_State *L)
{
    int i;
    int top = lua_gettop(L);
    for (i = 1; i <= top; i++) {  /* repeat for each level */
       int t = lua_type(L, i);
       switch (t) {
       case LUA_TSTRING:  /* strings */
           printf("`%s'", lua_tostring(L, i));
           break;
       case LUA_TBOOLEAN:  /* booleans */
           printf(lua_toboolean(L, i) ? "true" : "false");
           break;
       case LUA_TNUMBER:  /* numbers */
           printf("%g", lua_tonumber(L, i));
           break;
       default:  /* other values */
           printf("%s", lua_typename(L, t));
           break;
       }
       printf("  ");  /* put a separator */
    }
    printf("\n");     /* end the listing */
}

typedef struct list{
	int n;
	int idx;
	int buffer[0];
}list; 

static int ls_iter(lua_State* L)
{
	list* data =NULL;
	//stackDump(L);
	data = *(list**)lua_touserdata(L, lua_upvalueindex(2));
	if (data->idx < data->n)
	{
		lua_pushnumber(L, (data->buffer)[data->idx]);
		data->idx += 1;
		return 1;
	}
	return 0;
}

static int ls_dir(lua_State* L)
{
	int i = 0;
	const char* path = luaL_checkstring(L, 1);
	list** data = (list**)lua_newuserdata(L, sizeof(list*));
	//stackDump(L);
	luaL_getmetatable(L, "LuaBook.dir");
	//stackDump(L);
    lua_setmetatable(L, -2);
	//stackDump(L);


	*data = (list*)malloc(sizeof(list) + 10*sizeof(int));
	if(*data == NULL)
	{
		luaL_error(L, "malloc errro\n");
	}
	(*data)->n = 10;
	(*data)->idx = 0;
	
	for(i=0; i<(*data)->n; ++i)
	{
		((*data)->buffer)[i]= i;
	}

	lua_pushcclosure(L, ls_iter, 2);
	//stackDump(L);
	return 1;
}

static int ls_gc(lua_State* L)
{
	list* data = *(list**)lua_touserdata(L,1);
	if (data)
	{
		free(data);
	}
	return 0;
}

int luaopen_dir(lua_State* L)
{
	luaL_newmetatable(L, "LuaBook.dir");
    /* set its __gc field */
    lua_pushstring(L, "__gc");
    lua_pushcfunction(L, ls_gc);
    lua_settable(L, -3);

    /* register the `dir' function */
    lua_pushcfunction(L, ls_dir);
    lua_setglobal(L, "dir");
    return 0;
}